package docker

import (
	"bufio"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/client"

	"xindl/deployer/pkg/errors"
	"xindl/deployer/pkg/logger"
)

// 服务状态
type ServiceStatus struct {
	ID           string
	Name         string
	Image        string
	Replicas     uint64
	DesiredTasks uint64
	RunningTasks uint64
	FailedTasks  uint64
	UpdateStatus string
	LastError    string
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

// 日志条目
type LogEntry struct {
	Timestamp time.Time
	Stream    string // stdout/stderr
	Message   string
}

// 服务监控器
type ServiceMonitor struct {
	client *client.Client
	log    *logger.Logger
}

// 创建服务监控器
func NewServiceMonitor(client *client.Client, debug bool) *ServiceMonitor {
	return &ServiceMonitor{
		client: client,
		log:    logger.New(debug).WithPrefix("服务监控"),
	}
}

// 获取服务状态
func (m *ServiceMonitor) GetServiceStatus(ctx context.Context, serviceName string) (*ServiceStatus, error) {
	// 获取服务信息
	service, _, err := m.client.ServiceInspectWithRaw(ctx, serviceName, types.ServiceInspectOptions{})
	if err != nil {
		return nil, errors.NewErrorWithCode(errors.ErrCodeDocker, "获取服务信息失败").
			WithContext("service", serviceName).
			WithContext("error", err.Error())
	}

	// 获取服务任务
	tasks, err := m.getTasks(ctx, serviceName)
	if err != nil {
		return nil, errors.WithContext(err, "service", serviceName)
	}

	// 统计任务状态
	var running, failed uint64
	for _, task := range tasks {
		switch task.Status.State {
		case swarm.TaskStateRunning:
			running++
		case swarm.TaskStateFailed, swarm.TaskStateRejected:
			failed++
		}
	}

	// 构建状态信息
	status := &ServiceStatus{
		ID:           service.ID,
		Name:         service.Spec.Name,
		Image:        service.Spec.TaskTemplate.ContainerSpec.Image,
		DesiredTasks: *service.Spec.Mode.Replicated.Replicas,
		RunningTasks: running,
		FailedTasks:  failed,
		CreatedAt:    service.CreatedAt,
		UpdatedAt:    service.UpdatedAt,
	}

	// 添加更新状态
	if service.UpdateStatus != nil {
		status.UpdateStatus = string(service.UpdateStatus.State)
		if service.UpdateStatus.Message != "" {
			status.LastError = service.UpdateStatus.Message
		}
	}

	return status, nil
}

// 获取服务任务
func (m *ServiceMonitor) getTasks(ctx context.Context, serviceName string) ([]swarm.Task, error) {
	filter := filters.NewArgs()
	filter.Add("service", serviceName)

	tasks, err := m.client.TaskList(ctx, types.TaskListOptions{
		Filters: filter,
	})
	if err != nil {
		return nil, errors.NewErrorWithCode(errors.ErrCodeDocker, "获取服务任务失败").
			WithContext("service", serviceName).
			WithContext("error", err.Error())
	}

	return tasks, nil
}

// 监控服务状态变化
func (m *ServiceMonitor) WatchService(ctx context.Context, serviceName string) (<-chan *ServiceStatus, error) {
	statusChan := make(chan *ServiceStatus, MonitorBufferSize)

	go func() {
		defer close(statusChan)

		ticker := time.NewTicker(MonitorInterval)
		defer ticker.Stop()

		for {
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
				status, err := m.GetServiceStatus(ctx, serviceName)
				if err != nil {
					m.log.Error("获取服务状态失败: %v", err)
					continue
				}

				select {
				case statusChan <- status:
				case <-ctx.Done():
					return
				default:
					// 如果通道已满，丢弃旧的状态
					select {
					case <-statusChan:
						statusChan <- status
					default:
						m.log.Warn("状态通道已满，丢弃状态更新")
					}
				}
			}
		}
	}()

	return statusChan, nil
}

// 检查服务是否健康
func (m *ServiceMonitor) IsServiceHealthy(status *ServiceStatus) bool {
	if status == nil {
		return false
	}

	// 检查是否所有期望的副本都在运行
	if status.RunningTasks != status.DesiredTasks {
		return false
	}

	// 检查是否有失败的任务
	if status.FailedTasks > 0 {
		return false
	}

	// 检查更新状态
	if status.UpdateStatus != "" &&
		status.UpdateStatus != string(swarm.UpdateStateCompleted) {
		return false
	}

	return true
}

// 等待服务健康
func (m *ServiceMonitor) WaitForHealthy(ctx context.Context, serviceName string, timeout time.Duration) error {
	timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	ticker := time.NewTicker(MonitorInterval)
	defer ticker.Stop()

	for {
		select {
		case <-timeoutCtx.Done():
			return errors.NewErrorWithCode(errors.ErrCodeHealthTimeout, "等待服务健康超时").
				WithContext("service", serviceName).
				WithContext("timeout", timeout.String())
		case <-ticker.C:
			status, err := m.GetServiceStatus(ctx, serviceName)
			if err != nil {
				m.log.Warn("获取服务状态失败: %v", err)
				continue
			}

			if m.IsServiceHealthy(status) {
				return nil
			}

			m.log.Debug("服务状态: 运行中=%d/%d, 失败=%d, 更新状态=%s",
				status.RunningTasks, status.DesiredTasks,
				status.FailedTasks, status.UpdateStatus)
		}
	}
}

// 获取服务日志
func (m *ServiceMonitor) GetServiceLogs(ctx context.Context, serviceName string, tail string) ([]*LogEntry, error) {
	options := types.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Tail:       tail,
		Timestamps: true,
		Follow:     false,
	}

	// 获取服务任务
	tasks, err := m.getTasks(ctx, serviceName)
	if err != nil {
		return nil, err
	}

	var logs []*LogEntry
	for _, task := range tasks {
		if task.Status.State != swarm.TaskStateRunning {
			continue
		}

		// 获取容器日志
		reader, err := m.client.ContainerLogs(ctx, task.Status.ContainerStatus.ContainerID, options)
		if err != nil {
			return nil, errors.NewErrorWithCode(errors.ErrCodeDocker, "获取容器日志失败").
				WithContext("container", task.Status.ContainerStatus.ContainerID).
				WithContext("error", err.Error())
		}

		// 解析日志
		taskLogs, err := m.parseLogs(reader)
		reader.Close()
		if err != nil {
			m.log.Warn("解析容器日志失败: %v", err)
			continue
		}

		logs = append(logs, taskLogs...)
	}

	return logs, nil
}

// 解析Docker日志流
func (m *ServiceMonitor) parseLogs(reader io.Reader) ([]*LogEntry, error) {
	var logs []*LogEntry
	bufReader := bufio.NewReader(reader)

	for {
		// 读取日志头部（8字节）
		header := make([]byte, 8)
		_, err := io.ReadFull(bufReader, header)
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}

		// 解析日志头部
		// 第一个字节表示流类型：1=stdout, 2=stderr
		streamType := header[0]
		// 后面4个字节表示消息长度
		length := binary.BigEndian.Uint32(header[4:])

		// 读取消息内容
		message := make([]byte, length)
		_, err = io.ReadFull(bufReader, message)
		if err != nil {
			return nil, err
		}

		// 解析消息（格式：timestamp message）
		parts := strings.SplitN(string(message), " ", 2)
		if len(parts) != 2 {
			continue
		}

		// 解析时间戳
		timestamp, err := time.Parse(time.RFC3339Nano, parts[0])
		if err != nil {
			continue
		}

		// 创建日志条目
		entry := &LogEntry{
			Timestamp: timestamp,
			Message:   strings.TrimSpace(parts[1]),
		}

		// 设置流类型
		switch streamType {
		case 1:
			entry.Stream = "stdout"
		case 2:
			entry.Stream = "stderr"
		default:
			entry.Stream = "unknown"
		}

		logs = append(logs, entry)
	}

	return logs, nil
}

// 监控服务日志
func (m *ServiceMonitor) WatchLogs(ctx context.Context, serviceName string) (<-chan *LogEntry, error) {
	logChan := make(chan *LogEntry, MonitorBufferSize)

	go func() {
		defer close(logChan)

		options := types.ContainerLogsOptions{
			ShowStdout: true,
			ShowStderr: true,
			Follow:     true,
			Timestamps: true,
		}

		for {
			select {
			case <-ctx.Done():
				return
			default:
				// 获取最新的任务列表
				tasks, err := m.getTasks(ctx, serviceName)
				if err != nil {
					m.log.Error("获取服务任务失败: %v", err)
					time.Sleep(RetryInterval)
					continue
				}

				// 监控每个运行中的任务的日志
				for _, task := range tasks {
					if task.Status.State != swarm.TaskStateRunning {
						continue
					}

					reader, err := m.client.ContainerLogs(ctx, task.Status.ContainerStatus.ContainerID, options)
					if err != nil {
						m.log.Error("获取容器日志失败: %v", err)
						continue
					}

					// 解析并发送日志
					go func(reader io.ReadCloser) {
						defer reader.Close()
						logs, err := m.parseLogs(reader)
						if err != nil {
							m.log.Error("解析日志失败: %v", err)
							return
						}

						for _, entry := range logs {
							select {
							case <-ctx.Done():
								return
							case logChan <- entry:
							}
						}
					}(reader)
				}

				// 等待一段时间再重新获取任务列表
				time.Sleep(MonitorInterval)
			}
		}
	}()

	return logChan, nil
}
